<?php
/**
 *  * @author 韩天尊
 * Date: 2022/3/9
 * Email: <724700483@qq.com>
 */

namespace App\HttpController\Api;

use App\HttpController\BaseController;
use App\Toolys\CustomHttpException;
use App\Toolys\IpList;
use App\Toolys\VerifyCode\VerifyCode;
use EasySwoole\Component\Context\ContextManager;
use EasySwoole\EasySwoole\ServerManager;
use EasySwoole\Http\AbstractInterface\AbstractRouter;
use EasySwoole\Http\Message\Status;
use EasySwoole\HttpAnnotation\Exception\Annotation\ParamValidateError;
use EasySwoole\Session\Context;
use EasySwoole\Validate\Exception\Runtime;
use EasySwoole\Validate\Validate;
use Swoole\Exception;
use EasySwoole\Component\Di;
use Throwable;

abstract class ApiBase extends BaseController
{

    // public 才会根据协程清除
    public $currentTokenActionId;
    public $currentTokenOtherInfo;
    public $currentTokenServerInfo;


    public $requestApiData;//请求数据

    /*
     *  print_r("-------getRequestParam");echo PHP_EOL;
        print_r($this->request()->getRequestParam());echo PHP_EOL;//multipart/form-data,application/x-www-from-urlencoded
        print_r("-------getParsedBody");echo PHP_EOL;
        print_r($this->request()->getParsedBody());echo PHP_EOL;//multipart/form-data,application/x-www-from-urlencoded
        print_r("-------getBody()->__toString");echo PHP_EOL;
        print_r($this->request()->getBody()->__toString());echo PHP_EOL;//application/json,application/xml,application/x-www-from-urlencoded,multipart/form-data(会error)
    */

    public function onRequest(?string $action): ?bool
    {
        if (!parent::onRequest($action)) {return false;}
        $context = ContextManager::getInstance()->get(AbstractRouter::PARSE_PARAMS_CONTEXT_KEY);
        $get = $this->request()->getQueryParams();
        $body = json_decode($this->request()->getBody()->__toString(), 1);
        if (empty($body)){$body = $this->request()->getParsedBody();}
        $this->requestApiData = array_merge($get, $body, $context);//强制所有接口数据走validate验证
        return true;
    }

    public function afterAction(?string $actionName): void
    {
        parent::afterAction($actionName); // TODO: Change the autogenerated stub
        $request = $this->request();
        $fd = $request->getSwooleRequest()->fd;
        $ip = ServerManager::getInstance()->getSwooleServer()->getClientInfo($fd)['remote_ip'];
        $keyString = "{$ip}-{$request->getServerParams()['request_uri']}-{$request->getMethod()}";
        Di::getInstance()->get('fastCache_DelNx')($keyString);
    }

    /** 验证拦截输出
     * @param Validate $validate
     * @throws Runtime
     * @throws \Exception
     */
    public function validateInitApiData(Validate $validate)
    {
        if (!$validate->validate($this->requestApiData)){
            $errValidate = new ParamValidateError();
            $errValidate->setValidate($validate);
            throw $errValidate;
        }
        $this->requestApiData = arrayTransitionObject($this->requestApiData);
    }

    /** 检测非api数据用
     * @throws Runtime
     * @throws ParamValidateError
     */
    public function checkParamsData(Validate $validate, array $data): bool
    {
        if (!$validate->validate($data)){
            $errValidate = new ParamValidateError();
            $errValidate->setValidate($validate);
            throw $errValidate;
        }
        return true;
    }

    /** 获取客户端ip
     * @return mixed
     * *    author 韩天尊
     */
    public function getRequestIp()
    {
        $fd = $this->request()->getSwooleRequest()->fd;
        return \EasySwoole\EasySwoole\ServerManager::getInstance()->getSwooleServer()->connection_info($fd);
    }

    /** session相关
     * *    author 韩天尊
     */
    public function sessionClient(): ?Context
    {
        // 封装一个方法，方便我们快速获取 session context
        $sessionId = $this->request()->getAttribute('session');
        return \App\Toolys\Session::getInstance()->create($sessionId);

        // $this->sessionClient()->set('key', 'value');
        // 把 'test_session_key' 作为键，time() 的值作为值，保存在 session 中

        // $this->sessionClient()->get('key');
        // 从 session 中获取 key 为 'test_session_key' 的值

        // 获取 session 中所有数据
        // $ret = $this->sessionClient()->allContext();

        // $this->sessionClient()->del('key');
        // 删除 session 中 key 为 'test_session_key' 的值

        // 清空 session 中所有数据
        // $this->sessionClient()->flush();

        // 重新设置(覆盖) session 中的数据
        // $ret = $this->sessionClient()->setData([
        //    'test_session_key' => 1,
        //    'test_session_key1' => 2
        // ]);
    }

    /**
     * 图片验证码组合拳
     */
    /** 将验证码图片写入到前端
     * @param string $event 事件标识
     * *    author 韩天尊
     */
    public function getVerifyCodeImage(string $event)
    {
        (new VerifyCode())->verifyCode($event, $this->response());
        $this->writeBack->write_back_type = false;
    }

    /** 验证输入的图片验证码
     * @param string $event
     * @param string $code
     * *    author 韩天尊
     */
    public function checkVerifyCodeImage(string $event, string $code)
    {
        (new VerifyCode())->checkVerifyCode($event, $code, $this->request());
    }

    /** ip限流
     * @param $ip
     * @throws CustomHttpException
     * *    author 韩天尊
     */
    private function interceptIp($ip)
    {
        // 如果当前周期的访问频率已经超过设置的值，则拦截
        if (IpList::getInstance()->access($ip) > 15) {
            $hintMessage = "该ip: {$ip} 被限流";
            customHttpException(400, $hintMessage);
        }
    }

    /** ip到function频繁调用拦截
     * @param $ip
     * @param $request_uri
     * @throws CustomHttpException
     * @throws Throwable
     * *    author 韩天尊
     */
    private function interceptIpFunction($ip, $request_uri)
    {
        $limit = Di::getInstance()->get('auto_limiter');
        $token = $ip . $request_uri;
        //access 函数允许单独对某个 token 指定qps
        if (!$limit->access($token)) {
            $hintMessage = "ip-fun: {$token} 限流拦截";
            customHttpException(400, $hintMessage);
        }
    }

    /** 防抖拦截
     * @param $ip
     * @param $request_uri
     * @param $method
     * @throws CustomHttpException
     * @throws Throwable
     * *    author 韩天尊
     */
    private function antiShake($ip, $request_uri, $method)
    {
        $keyString = "{$ip}-{$request_uri}-{$method}";
        $antiShake = Di::getInstance()->get('fastCache_SetNx')($keyString, 180);
        if ($antiShake == false){
            customHttpException(400, "防抖拦截: {$keyString}");
        }
    }

    /** 拦截合并调用
     * @param array|string[] $methods
     * @throws CustomHttpException
     * @throws Throwable
     * *    author 韩天尊
     */
    public function intercept(array $methods=["ip","ipFun","antiShake"])
    {
        $request = $this->request();
        $fd = $request->getSwooleRequest()->fd;
        $ip = ServerManager::getInstance()->getSwooleServer()->getClientInfo($fd)['remote_ip'];
        $request_uri = $request->getServerParams()['request_uri'];
        $method = $request->getMethod();

        foreach ($methods as $item_method){
            switch ($item_method){
                case "ip":{
                    $this->interceptIp($ip);
                    break;
                }
                case "ipFun":{
                    $this->interceptIpFunction($ip, $request_uri);
                    break;
                }
                case "antiShake":{
                    $this->antiShake($ip, $request_uri, $method);
                    break;
                }
            }
        }


    }

}